package com.fanxuankai.commons.util;

import okhttp3.OkHttpClient;
import retrofit2.Call;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.http.GET;
import retrofit2.http.Query;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author fanxuankai
 */
public class Prometheus {
    public static class Client {
        private final Rest service;

        public Client(String baseUrl) {
            service = new Retrofit.Builder().baseUrl(baseUrl)
                    .client(new OkHttpClient.Builder().readTimeout(60, TimeUnit.SECONDS)
                            .connectTimeout(60, TimeUnit.SECONDS)
                            .writeTimeout(60, TimeUnit.SECONDS)
                            .build())
                    .addConverterFactory(GsonConverterFactory.create())
                    .build()
                    .create(Rest.class);
        }

        public Result.Vector query(String query) throws IOException {
            return service.query(query, null, null).execute().body();
        }

        public Result.Vector query(String query, String time) throws IOException {
            return service.query(query, time, null).execute().body();
        }

        public Result.Vector query(String query, String time, String timeout) throws IOException {
            return service.query(query, time, timeout).execute().body();
        }

        public Result.Matrix queryRange(String query, String start, String end, String step,
                                        String timeout) throws IOException {
            return service.queryRange(query, start, end, step, timeout).execute().body();
        }

        public Result.KeyValue series(String match, String start, String end) throws IOException {
            return service.series(match, start, end).execute().body();
        }
    }

    public interface Rest {
        /**
         * The following endpoint evaluates an instant query at a single point in time.
         *
         * @param query   Prometheus expression query string.
         * @param time    Evaluation timestamp. Optional.
         * @param timeout Evaluation timeout. Optional. Defaults to and is capped by the value of the -query.timeout
         *                flag.
         * @return Result.Vector
         */
        @GET("api/v1/query")
        Call<Result.Vector> query(@Query("query") String query, @Query("time") String time,
                                  @Query("timeout") String timeout);

        /**
         * The following endpoint evaluates an expression query over a range of time.
         *
         * @param query   Prometheus expression query string.
         * @param start   Start timestamp, inclusive.
         * @param end     End timestamp, inclusive.
         * @param step    Query resolution step width in duration format or float number of seconds.
         * @param timeout Evaluation timeout. Optional. Defaults to and is capped by the value of the -query.timeout
         *                flag.
         * @return Result.Matrix
         */
        @GET("api/v1/query_range")
        Call<Result.Matrix> queryRange(@Query("query") String query, @Query("start") String start,
                                       @Query("end") String end, @Query("step") String step, @Query("timeout") String timeout);

        /**
         * The following endpoint returns the list of time series that match a certain label set.
         *
         * @param match Repeated series selector argument that selects the series to return. At least one match[]
         *              argument must be provided.
         * @param start Start timestamp.
         * @param end   End timestamp.
         * @return Result.KeyVal
         */
        @GET("api/v1/series")
        Call<Result.KeyValue> series(@Query("match[]") String match, @Query("start") String start,
                                     @Query("end") String end);
    }

    public static class Result {
        public static class Vector {
            private String status;
            private Data data;

            public String getStatus() {
                return status;
            }

            public void setStatus(String status) {
                this.status = status;
            }

            public Data getData() {
                return data;
            }

            public void setData(Data data) {
                this.data = data;
            }

            public static class Data {
                private String resultType;
                private List<VectorResult> result;

                public String getResultType() {
                    return resultType;
                }

                public void setResultType(String resultType) {
                    this.resultType = resultType;
                }

                public List<VectorResult> getResult() {
                    return result;
                }

                public void setResult(List<VectorResult> result) {
                    this.result = result;
                }

                public static class VectorResult {
                    private Map<String, String> metric;
                    private List<Float> value;

                    public Map<String, String> getMetric() {
                        return metric;
                    }

                    public void setMetric(Map<String, String> metric) {
                        this.metric = metric;
                    }

                    public List<Float> getValue() {
                        return value;
                    }

                    public void setValue(List<Float> value) {
                        this.value = value;
                    }
                }
            }
        }

        public static class Matrix {
            private String status;
            private Data data;

            public static class Data {
                private String resultType;
                private List<MatrixResult> result;

                public String getResultType() {
                    return resultType;
                }

                public void setResultType(String resultType) {
                    this.resultType = resultType;
                }

                public List<MatrixResult> getResult() {
                    return result;
                }

                public void setResult(List<MatrixResult> result) {
                    this.result = result;
                }
            }

            public static class MatrixResult {
                private Map<String, String> metric;
                private List<List<Float>> values;

                public Map<String, String> getMetric() {
                    return metric;
                }

                public void setMetric(Map<String, String> metric) {
                    this.metric = metric;
                }

                public List<List<Float>> getValues() {
                    return values;
                }

                public void setValues(List<List<Float>> values) {
                    this.values = values;
                }
            }
        }

        public static class KeyValue {
            private String status;
            private List<Map<String, String>> data;

            public String getStatus() {
                return status;
            }

            public void setStatus(String status) {
                this.status = status;
            }

            public List<Map<String, String>> getData() {
                return data;
            }

            public void setData(List<Map<String, String>> data) {
                this.data = data;
            }
        }
    }
}
